/*
 * KDTNode.java
 *
 * Created on October 9, 2007, 8:22 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package Raytracing.Accelerators;

import Raytracing.BoundingBox;
import Raytracing.Intersectable;
import Raytracing.Ray;
import processing.core.*;
import java.util.*;

public class KDTNode {

    static final int X_AXIS = 0;
    static final int Y_AXIS = 1;
    static final int Z_AXIS = 2;
    static final int NO_AXIS = 3;
    private static final boolean DEBUG = false;

    ArrayList <Intersectable> objList;
    KDTNode left = null, right = null;
    int axis;
    float splitPlane;

    public void add(ArrayList newObs, int maxSize, int depth, int maxDepth, BoundingBox bounds) {


        if (objList == null) {
            objList = new ArrayList();
        }
        Intersectable insert;
        for (int i = 0; i < newObs.size(); i++) {
            insert = (Intersectable) newObs.get(i);
            if (insert.inside(bounds)) {
                objList.add(insert);
            }

        }

       // System.out.println("inserted" + objList.size() + " of " + newObs.size());

        //do we have too many elements in our node? Then split!
        if (objList.size() > maxSize && depth < maxDepth) {

            //split the largest plane...
            if (bounds.getXDim() > bounds.getYDim()) {
                if (bounds.getZDim() > bounds.getXDim()) {
                    axis = Z_AXIS;
                  //  System.out.println("Split Z");
                } else {
                    axis = X_AXIS;
                 //   System.out.println("Split X");
                }
            } else if (bounds.getYDim() > bounds.getZDim()) {
                axis = Y_AXIS;
              //  System.out.println("Split Y");
            } else {
                axis = Z_AXIS;
                //System.out.println("Split Z");
            }



            //create children
            right = new KDTNode();
            left = new KDTNode();

            float tmp = 0;

            switch (axis) {
                case X_AXIS:
                    //perform the split
                    //for now, split in the middle.  we'll come up with something better later.
                    splitPlane = (bounds.min.x + bounds.max.x) / 2;
                    tmp = bounds.min.x;
                    bounds.min.x = splitPlane;
                    right.add(objList, maxSize, depth + 1, maxDepth, bounds);
                    bounds.min.x = tmp;
                    tmp = bounds.max.x;
                    bounds.max.x = splitPlane;
                    left.add(objList, maxSize, depth + 1, maxDepth, bounds);
                    bounds.max.x = tmp;
                    break;
                case Y_AXIS:
                    splitPlane = (bounds.min.y + bounds.max.y) / 2;
                    tmp = bounds.min.y;
                    bounds.min.y = splitPlane;
                    right.add(objList, maxSize, depth + 1, maxDepth, bounds);
                    bounds.min.y = tmp;
                    tmp = bounds.max.y;
                    bounds.max.y = splitPlane;
                    left.add(objList, maxSize, depth + 1, maxDepth, bounds);
                    bounds.max.y = tmp;
                    break;
                case Z_AXIS:
                    splitPlane = (bounds.min.z + bounds.max.z) / 2;
                    tmp = bounds.min.z;
                    bounds.min.z = splitPlane;
                    right.add(objList, maxSize, depth + 1, maxDepth, bounds);
                    bounds.min.z = tmp;
                    tmp = bounds.max.z;
                    bounds.max.z = splitPlane;
                    left.add(objList, maxSize, depth + 1, maxDepth, bounds);
                    bounds.max.z = tmp;

                    break;
            }

            //add all the objects to each of the children -- they will discard
            //them if they don't fit

            objList = null;

        }



    }

    public void draw(PApplet g) {


        if (left != null) {
            left.draw(g);
            right.draw(g);
        }
    }

    protected boolean procTerminal(Ray state, float t0, float t1) {



        //loop through the leaves and return the nearest one

        //if (DEBUG) System.out.println("Processing terminal node with " + objList.size() + " objects." );
        //if (DEBUG) System.out.println("     Min Ray: " + t0 + " Max Ray: " + t1 );
        float min = t1;
        float tu = 0;
        float tv = 0;

        PVector tn = null, sn = null, nn = null;

        Intersectable obj, nearest = null;

        //iterate through scene elements and record nearest intersection
        for (int i = 0; i < objList.size(); i++) {

            obj = objList.get(i);

            if (obj.intersect(state)) {

                //if we are within the appropriate distance 
                if (state.distance >= t0 && state.distance <= min) {

                    min = state.distance;
                    tu = state.tu;
                    tv = state.tv;

                    tn = state.tn;
                    sn = state.sn;
                    nn = state.normal;

                    nearest = obj;
                }
            }
        }

        //we hit something
        if (nearest != null) {
            state.distance = min;
            state.occluder = nearest;
            state.tu = tu;
            state.tv = tv;

            state.normal = nn;
            state.sn = sn;
            state.tn = tn;
            return true;
        }
        //we missed
        return false;
    }
}
